﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.ComponentModel;
using System.Threading;


namespace IVisual._SC.AutoDiscovery
{
    /// <summary>
    /// Client für das AD
    /// </summary>
    public class cls_AutoDiscovery_Client
    {
        // Port für das AD. MUSS bei Cleint und Server gleich sein ;-)
        public int AutoDiscoveryPort = 18500;
        
        private int AutoDiscoveryTimeout = 10000;

        // Erkennungsmerkmal des Dienstes:
        // 0x1, 0x2, 0x3 == Primary-Server
        // 0x2, 0x3, 0x4 == Visual-Server
        public byte[] packetBytes = new byte[] { 0x1, 0x2, 0x3 };

        // Rückgabewerte
        public string ServerAddress = String.Empty;
        public int ServerPort = 0;

        private BackgroundWorker worker;

        public cls_AutoDiscovery_Client(ref BackgroundWorker worker)
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            this.worker = worker;
            worker.ReportProgress(1, "AutoDiscovery:: " + AutoDiscoveryPort + "/UDP");
            
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "AutoDiscovery-Client-Port: " + AutoDiscoveryPort + "/UDP");
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
        }

        /// <summary>
        /// Starte Server Suche (BW Methode)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void Start(object sender, DoWorkEventArgs e)
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            try
            {
                if (ServerAddress == String.Empty)
                {
                    this.worker.ReportProgress(2, "AutoDiscovery::Suche nach Server..");

                    // Broadcast query
                    e.Result = sendBroadcastSearchPacket();
                }
            }
            catch (Exception ex)
            {
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.ERROR, ex.Message);
            }

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
        }

        /// <summary>
        /// Broadcastet die Anfrage
        /// </summary>
        /// <returns>OK?</returns>
        private bool sendBroadcastSearchPacket()
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "start");

            bool returnVal = false;
            UdpClient udp = new UdpClient();
            udp.EnableBroadcast = true;

            udp.Client.ReceiveTimeout = AutoDiscoveryTimeout;

            IPEndPoint groupEP = new IPEndPoint(IPAddress.Parse("255.255.255.255"), AutoDiscoveryPort);

            try
            {
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Broadcasting via UDP");

                udp.Send(packetBytes, packetBytes.Length, groupEP);

                byte[] receiveBytes = udp.Receive(ref groupEP);

                string returnData = Encoding.ASCII.GetString(receiveBytes, 0, receiveBytes.Length);
                if (returnData.Substring(0, 3) == "ACK")
                {
                    string[] splitRcvd = returnData.Split(' ');
                    this.worker.ReportProgress(3, "AutoDiscovery::Antwort-Server " + splitRcvd[1] + ":" + splitRcvd[2]);
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Antwort-Server " + splitRcvd[1] + ":" + splitRcvd[2]);

                    ServerAddress = splitRcvd[1];
                    ServerPort = Convert.ToInt16(splitRcvd[2]);
                    returnVal = true;
                }
                else if (returnData.Substring(0, 3) == "NAK")
                {
                    this.worker.ReportProgress(3, "AutoDiscovery::INVALID REQUEST");
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Invalid Request");
                }
                else
                {
                    this.worker.ReportProgress(3, "AutoDiscovery::Garbage Received?");
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Ungültiges Packet");
                }
            }
            catch (SocketException e)
            {
                this.worker.ReportProgress(1, "AutoDiscovery::Timeout. Retrying "+e.Message);
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Timeout. Retrying " + e.Message);
            }

            udp.Close();

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
            return (returnVal);
        }
    }
}
